<?php
/**************
模型-基类
主要负责与MYSQL数据库的交互
lsj 2009-12-26
*************/

if(!defined('APP_HOME')) exit('Access Denied');

class lib_model
{
	protected $db = null;#当前数据库操作类
	protected $dbs = array();#所有数据库的操作类

	protected $tbl = null;#当前模型操作的主表名称
	protected $key = array();#主表的外键与对应要连接的表[array('a'=>'b.a','c'=>'d.c')]

	protected $num = 0;#操作的记录数
	protected $from = 0;#查询时，记录开始处
	protected $pnum = 0;#分页查询的条数
	protected $total = null;#当前查询条件下的数据总数

	protected $join = '';#连接语句
	protected $groupBy = '';#GROUP BY语句
	protected $orderBy = '';#ORDER BY语句
	protected $where = '1';#WHERE条件语句
	

	/**
	 * 加载数据库类
	 * $dbConifigFile: 数据库配置文件名[位于conf目录下]
	 */
	public function __construct($dbConifigFile = 'mysql')
	{
		#加载数据库配置文件
		$thedb = loadConfig($dbConifigFile);

		#根据配置做多数据库连接，还是单数据库连接
		if(count($thedb) > 1){#连接多个
			foreach($thedb as $key=>$val){
				$this->dbs[$key] = new lib_Class_Mysql($val);
			}
		}elseif(is_array($thedb)){#只连接一个
			$this->db = new lib_class_mysql(array_shift($thedb));
		}

		unset($thedb);
		$this->_init();
	}

	/**
	 * 实例化时的最后一个工作
	 */
	public function _init()
	{

	}
	
	/**
	 * 设置是否为调试状态
	 * 调试时打印构造好的SQL语句[0：关 1：开]
	 */
	public function setDebug($d = 1)
	{
		#接通数据库操作类的DEBUG，在执行SQL之前会打印出SQL语句
		$this->db->setDebug($d);
		return $this;
	}

	/**
	 * 选择数据库操作类
	 * 当有多个数据库时，进行此操作以切换当前的数据库
	 * $name: 数据库配置文件中的数据库的名
	 */
	public function selectDb($name)
	{
		if($this->dbs[$name]){
            $this->db = $this->dbs[$name];
		    return $this->db;
        }else{
            throw new Exception('The db server ['.$name.'] is not exists.');
        }
        return false;
	}

	/**
	 * 构造连接条件
	 * $tbl: 要连接的表名
	 * $type: 连接方式[left right inner]
	 */
	public function joinTable($tbl, $type = 'LEFT')
	{
		if(empty($this->key)) return false;
		$type = strtoupper($type);
		foreach($this->key as $k=>$v){
			if(strpos($v, '`') !== false) $v = str_replace('`', '', $v);
			if($tbl == current(explode('.', $v))){
				$v = str_replace('.', '`.`', $v);
				if($this->join) $this->join .= ' ';
				$this->join .= $type.' JOIN `'.$tbl.'` ON `'.$this->tbl.'`.`'.$k.'`=`'.$v.'`';
				return $this;
			}
		}
		return false;
	}
	
	/**
	 * 设置查询的GROUP BY条件
	 * $group: 分组字段，字符串或数组
	 */
	public function setGroupBy($group)
	{
		if(is_string($group)){
			if(strpos($group, '`') === false){
				$group = str_replace('.','`.`',$group);
				$group = '`'.$group.'`';
			}
			$this->groupBy .= $group;
		}elseif(is_array($group)){
			foreach($group as $g=>$p){
				if('' != $this->groupBy) $this->groupBy .= ',';
				if(is_string($g)) $this->groupBy .= '`'.$g.'`.`'.$p.'`';
				else $this->groupBy .= '`'.$p.'`';
			}
		}
		return $this;
	}
	
	/**
	 * 设置查询的ORDER BY条件
	 * $order: 排序字段，字符串或数组
	 * $mode: 排序方式 ASC、DESC
	 */
	public function setOrderBy($order, $mode = 'ASC')
	{
		if(is_string($order)){
			if(strpos($order, '`') === false){
				$order = str_replace('.','`.`',$order);
				$order = '`'.$order.'`';
			}
			$this->orderBy .= $order . ' ' . $mode;
		}elseif(is_array($order)){
			foreach($order as $g=>$p){
				if('' != $this->orderBy) $this->orderBy .= ',';
				if(is_string($g)) $this->orderBy .= '`'.$g.'`.`'.$p.'` '.$mode;
				else $this->orderBy .= '`'.$p.'` '.$mode;
			}
		}
		return $this;
	}
	
	/**
	 * 设置查询的条数及开始位置
	 * $n:查询的条数
	 * $f:开始位置
	 */
	public function setLimit($n = 15, $f = 0)
	{
		$this->num = $n;
		$this->from = $f;
		$this->pnum = $n;
		return $this;
	}

    /**
	 * 构造WHERE查询条件
	 * $name:字段名
	 * $value:字段值
	 * $con:双目运算符:is,no,max,min,lmax,lmin，自动识别like或in条件
	 */
	public function setWhere($name, $value, $con='is')
	{
		#格式化字段名，并为其加‘`’符号
		$name = str_replace(array("\t","\n","\r",' '), '', $name);
		if(strpos($name, '`') === false){
			$name = str_replace('.', '`.`', $name);
			$name = '`'.$name.'`';
		}

		#找到合适的运算符
		$con = strtolower($con);
		$conArr = array('no'=>'<>','not'=>'<>','max'=>'>','min'=>'<','lmax'=>'>=','lmin'=>'<=',);
        if($con == 'is'){
            $val = str_replace(array('\%', '\_'), '', $value);
			if(strpos($val, '%') !== false || strpos($val, '_') !== false){
				$con = 'like';
			}elseif(strpos($value, ',') !== false){
				$con = 'in';
				$value = '('.$value.")";
			}else{
				$con = '=';
			}
		}else{
			$con = $conArr[$con];
		}

		#构造WHERE语句
		if(is_string($value)){
			$this->where .= ' AND '.$name.$con."'".addslashes($value)."'";
		}else{
			$this->where .= ' AND '.$name.$con.$value;
		}

		return $this;
	}

	/**
	 * 查询
	 * $sel: 字符串与数组
	 * 返回：结果集[一列或一条或多条]
	 */
	public function select($sel = '*')
	{
		$sqlStr = '';
		$selStr = $sel;

		#构造SELECT字段
		if(is_array($sel)){
			$selStr = '';
			foreach ($sel as $key=>$val){
				if('' != $selStr) $selStr .= ',';
				$selStr .= $this->parseSelect($val, $key);
			}
		}
		
		#格式化
		if(strpos($selStr, '`') === false){
			$selStr = $this->addQuot($selStr);
		}
		
		#组合SQL
		$sqlStr = 'SELECT ' . $selStr . ' FROM ' . '`' . $this->tbl . '`';
		if($this->join) $sqlStr .= $this->join;
		if($this->where) $sqlStr .= ' WHERE ' . $this->where;
		if($this->groupBy) $sqlStr .= ' GROUP BY ' . $this->groupBy;
		if($this->orderBy) $sqlStr .= ' ORDER BY ' . $this->orderBy;

		#添加LIMIT
		if($this->num > 0) $sqlStr .= ' LIMIT '.$this->from.','.$this->num;
		//echo $sqlStr;
		#进行查询
		if($this->num == 1){
			if(strpos($selStr, ',') !== false || (strpos($selStr, '*') !== false && strpos($selStr, '(*)') === false)){
				$result = $this->db->selectRow($sqlStr);
			}else{
				$result = $this->db->selectOne($sqlStr);
			}
		}else{
			$result = $this->db->select($sqlStr);
		}

		#复原条件变量
		$this->resetSQL();

		return $result;
	}

	/**
	 * 修改
	 * $data: 数组格式
	 * 返回：数字[影响条数] 或 FALSE
	 */
	public function update($data)
	{
		$result = $this->db->update($data, $this->tbl, $this->where);
        
		#复原条件变量，尽管没有什么可复原的，以免有误操作
		$this->resetSQL();
		
		return $result;
	}

	/**
	 * 写入
	 * $data: 数组格式
	 * 返回：数字[新增的ID或影响条数] 或 FALSE;
	 */
	public function insert($data)
	{
		$result = $this->db->insert($data, $this->tbl);
		
		#复原条件变量，尽管没有什么可复原的，以免有误操作
		$this->resetSQL();
		
		return $result;
	}

    /**
	 * 删除
	 * 返回：数字[影响条数] 或 FALSE
	 */
	public function delete()
	{
		$sql = 'DELETE FROM '.$this->tbl.' WHERE '.$this->where;
		$result = $this->db->query($sql);
		
		#复原条件变量，尽管没有什么可复原的，以免有误操作
		$this->resetSQL();
		
		if($result) return $this->db->getAffectedRows();
		return false;
	}
	
	/**
	 * 原始的查询全部结果集
	 * 返回：二维数组[表的多条记录]
	 */
	public function selectAll($sql, $mod = 'array', $index = 'string')
	{
		return $this->db->select($sql, $mod, $index);
	}

	/**
	 * 原始的查询一行结果集
	 * 返回：一维数组[表的一条记录]
	 */
	public function selectRow($sql, $mod = 'array', $index = 'string')
	{
		return $this->db->selectRow($sql, $mod, $index);
	}

	/**
	 * 原始的查询一个结果集
	 * 返回：字符串或数字[列的一个值]
	 */
	public function selectOne($sql)
	{
		return $this->db->selectOne($sql);
	}

	/**
	 * 执行SQL语句
	 * 返回：资源标识符
	 */
	public function query($sql)
	{
		return $this->db->query($sql);
	}

	/**
	 * 取当前查询条件下的数据总数
	 * 返回：总条数
	 */
	public function counts()
	{
		$sql = '';
		if(!$this->db->getQueryString()){
			$sql = 'SELECT COUNT(*) FROM ';
			$sql .= $this->tbl . ' WHERE ' . $this->where;
		}
		if($this->total === null) $this->total = $this->db->count($sql);
		if(!$this->total) $this->total = 0;
		return $this->total;
	}

	/**
	 * 打印分页栏
	 * $p: 当前页码
	 * $style: 分页的样式
	 * $pageWidth: 分页栏显示的页码数
	 * $url: 页码的链接页面，会在后边附加上页码值
	 * 返回：分页栏的HTML代码
	 */
	public function pager($p, $style=12, $pageWidth = 5, $url='')
	{
		$this->counts();

		$page = new lib_class_page;
		$page->set_style($style);
		$page->set_page($this->pnum, $pageWidth, $url);
		$result = $page->print_page($this->total, $p);

		if(2 === URL_MODE){
			$result = str_replace(array(URL_CTRL_NAME.'=', URL_ACT_NAME.'='), '', $result);
		 	$result = str_replace(array('?', '&', '='), '/', $result);
		}
		return $result;
	}

    /*
	#复原条件变量
	*/
	public function resetSQL()
	{
		$this->from = 0;
		$this->num = 0;
		$this->join = '';
		$this->groupBy = '';
		$this->orderBy = '';
		$this->where = '1';
		$this->total = null;
		return $this;
	}
	
	/**
	 * 转义成字符串条件 
	 * $str: 条件字符串 
	 * 返回：带有\号的条件字符串
	 */
	public function escapeString($str)
	{
		return mysql_escape_string($str);
	}
	
	/**
	 * 转义成LIKE条件 
	 * $str: 条件字符串 
	 * 返回：带有%号的条件字符串
	 */
	public function escapeLike($str)
	{
		$str = mysql_escape_string($str);
		$str = str_replace('　', ' ', $str);
		$str = preg_replace(" {2,}", ' ', $str);
		return '%' . str_replace(array('%', '_', ' ',), array('\%','\_', '%'), $str) . '%';
	}

	/**
	 * 解析SELECT的字段，将表名自动加入到对应字段名前
	 * $str: 要查询的表中字段串（正常的查询语法，如：as,count,sum,distinct,+,-）
	 * $d: 表名
	 * 返回：字符串[表名自动加入到对应字段名前]
	 */
	protected function parseSelect($str, $d)
	{
		$st = '';
		$d .= '.';
		if(strpos($str , 'distinct') !== false){
			$st = 'distinct ';
			$str = str_replace('distinct ', '', $str);
		}
		$str = str_replace(array("\t", "\n", "\r"), ' ', $str);
		if(strpos($str, ' as ') !== false) $str = str_replace(' as ', '$',$str);
		$str = str_replace(' ', '', $str);
		if($str == '*') return $d.$str;
		$str = str_replace(array('distinct(', '(*)'), array('@', '#'),$str);
		$str = str_replace(array('+', '-', '*', '/', '%'), array('+,', '-,', '*,', '/,', '%,'), $str);
		$str = $d.str_replace(array(',', '('), array(','.$d, '('.$d), $str);
		$str = str_replace(array('+,', '-,', '*,', '/,', '%,', '$', '@', '#'), array('+', '-', '*', '/', '%', ' as ', 'distinct '.$d, '(*)'), $str);
		$str = preg_replace("/(".$d."(\d{1,}))/i", "$2", $str);
		$str = preg_replace("/(".$d."(\w{0,}\())/i", "$2", $str);
		return $st.$str;
	}

	/**
	 * 为要查询出的字段加[`]符号
	 * $str：不带[`]符号的查询字段名，不包含selec 与 from
	 * 返回：带[`]符号的字符串
	 */
	protected function addQuot($str)
	{
		$str = preg_replace("/\s+/", ' ', $str);
		$str = str_replace(array(' as ',' AS '), array('@as@','@AS@'), $str);
		$str = str_replace(array(' distinct ',' DISTINCT ','distinct ','DISTINCT '), array('@distinct@','@DISTINCT@','distinct@','DISTINCT@'), $str);
		$str = str_replace(' ', '', $str);
		$str = str_replace('@', ' ', $str);
		$str = str_replace(array('-','+',' as ','/',' AS ',',','.','(',')','distinct ','DISTINCT '), array('`-`','`+`','` as `','`/`','` AS `','`,`','`.`','(`','`)','distinct `','DISTINCT `'), $str);
		$str = str_replace('.`*`', '.*', $str);
		$str = str_replace('`*`,', '*,', $str);
		$str = str_replace(array(')`','`distinct','`DISTINCT'), array(')','distinct','DISTINCT'), $str);
		$str = preg_replace("/(\w)(\*)(\w)/", '\\1`*`\\3', $str);
		$str = preg_replace("/`(\w+)\(`/", '\\1(`', $str);
		$str = preg_replace("/`(\d+\.?\d*)`/", '\\1', $str);
		$str = str_replace('`*`', '*', $str);
		if(strpos($str, ',') === false) return $str;
		if(strpos($str, 'distinct') !== 0 && strpos($str, 'DISTINCT') !== 0) $str = '`'.$str;
		return $str.'`';
	}



}




